package cn.edu.hznu.util.network;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Optional;

public class ChatRoomServer {
	private Selector selector;
	private final static int port = 9999;
	private static Charset charset = Charset.forName("utf-8");
	private static HashSet<String> users = new HashSet<String>();
	private static String USER_EXIST = "System message: user exist, please change a name";
	private static String USER_CONTENT_SPILIT = "#@#";
	private static boolean flag = false;
	
	public void init() throws IOException {
		selector = Selector.open();
		ServerSocketChannel server = ServerSocketChannel.open();
		server.bind(new InetSocketAddress(port));
		server.configureBlocking(false);
		server.register(selector, SelectionKey.OP_ACCEPT);
		
		System.out.println("Server is listening now....");
		
		while (true) {
			if (selector.select() == 0) {
				continue;
			}
			
			for (Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
					keyIterator.hasNext(); ) {
				processSelectionKey(server, (SelectionKey) keyIterator.next());
				keyIterator.remove();
			}
		}
	}
	
	private void processSelectionKey(ServerSocketChannel server, SelectionKey key) throws IOException {
		if (key.isAcceptable()) {
			SocketChannel socketChannel = server.accept();
			socketChannel.configureBlocking(false);
			socketChannel.register(selector, SelectionKey.OP_READ);
			key.interestOps(SelectionKey.OP_ACCEPT);
			
			System.out.println("Server is listening from client :" + socketChannel.getRemoteAddress());
			
			socketChannel.write(charset.encode("Please input you name."));
		}
		if (key.isReadable()) {
			try (SocketChannel socketChannel = (SocketChannel) key.channel()) {
				ByteBuffer buf = ByteBuffer.allocate(1024);
				StringBuilder content = new StringBuilder();
				while (socketChannel.read(buf) != -1) {
					buf.flip();
					content.append(charset.decode(buf));
					
					System.out.println("Server is listening from client "
							+ socketChannel.getRemoteAddress() + "data rev is: " + content);
					
					key.interestOps(SelectionKey.OP_READ);
				}
				if (content.length() > 0) {
					Optional.ofNullable(content.toString().split(USER_CONTENT_SPILIT))
							.ifPresent(arrContent -> {
								if (arrContent.length == 1) {
									String name = arrContent[0];
									if (users.contains(name)) {
										try {
											socketChannel.write(charset.encode(USER_EXIST));
										} catch (IOException e) {
											e.printStackTrace();
										}
									} else {
										users.add(name);
										String message = "Welcome " + name + 
												" to chatroom! Online numbers: " + onlineNum(selector);
										broadCast(selector, null, "Welcome " + message);
									}
								} else if (arrContent.length > 1) {
									String name = arrContent[0];
									String message = content.substring(name.length() + USER_CONTENT_SPILIT.length());
									message = name + " say " + message;
									if (users.contains(name)) {
										broadCast(selector, socketChannel, message);
									}
											
								}
							});
					
				}
			} catch (IOException e) {
				key.cancel();
			}
		}
	}
	
	private static int onlineNum(Selector selector) {
		return selector.keys()
					   .stream()
					   .filter(key -> key.channel() instanceof SocketChannel)
					   .mapToInt(e -> 1)
					   .sum();
	}
	
	private static void broadCast(Selector selector, SocketChannel except, String content) {
		selector.keys()
				.forEach(key -> {
					Channel targetChannel = key.channel();
					if (targetChannel instanceof SocketChannel && targetChannel != except) {
						try {
							((SocketChannel) targetChannel).write(charset.encode(content));
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				});
	}
	
	public static void main(String[] args) {
		try {
			new ChatRoomServer().init();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	

}






















